{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9478f9bc",
   "metadata": {},
   "source": [
    "# Sample code scripts for LangChain and LangGraph\n",
    "\n",
    "github source:\n",
    "- **[LangChain](https://github.com/langchain-ai/langchain)**\n",
    "- **[LangGraph](https://github.com/langchain-ai/langgraph)**\n",
    "\n",
    "LangChain intro: https://docs.langchain.com.cn/docs/introduction/\n",
    "\n",
    "LangGraph intro and tutorial: https://langgraphcn.org/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4f44a13",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "1479ed32",
   "metadata": {},
   "source": [
    "## A sample test for LangGraph\n",
    "\n",
    "reference: https://blog.csdn.net/forevercui/article/details/145876616\n",
    "\n",
    "这是一个简单的使用 LangGraph 构建 Agent 组件的案例，用于回答用户的问题。它的结构是 \n",
    "```bash\n",
    "START -> chatbot -> END\n",
    "```\n",
    "\n",
    "该案例中就包含了 LangGraph 的几个基础组件\n",
    "1. 状态, state: 可以是一个简单的字典或者Pydantic模型。状态包含了应用运行时需要的所有信息以及模型生成的信息;\n",
    "2. 节点, node: 节点通常是Python函数,用于处理状态并返回更新后的状态;\n",
    "3. 边, edge: 边定义了节点之间的连接关系和路由逻辑."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1a2d55ab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Assistant: 中国有多所顶尖大学在不同领域各具优势，以下是几所公认的顶尖高校及其特点：\n",
      "\n",
      "1. **清华大学**  \n",
      "   - **优势学科**：工程、计算机科学、材料科学、建筑、经济管理等。  \n",
      "   - **国际排名**：常年位居中国内地高校榜首（如QS、THE等排名）。  \n",
      "   - **特色**：以理工科见长，被誉为“中国MIT”，科研实力和产学研结合突出。\n",
      "\n",
      "2. **北京大学**  \n",
      "   - **优势学科**：人文社科、基础科学（数学、物理、化学）、医学、法学等。  \n",
      "   - **国际排名**：与清华并列国内前两名，人文社科领域尤为顶尖。  \n",
      "   - **特色**：综合性强，思想自由、学术底蕴深厚。\n",
      "\n",
      "3. **其他顶尖高校**（部分领域领先）：  \n",
      "   - **复旦大学**/**上海交通大学**：华东地区双雄，复旦偏文理，上交强于工科和医学。  \n",
      "   - **浙江大学**：学科全面，农业工程、计算机、医学等突出。  \n",
      "   - **中国科学技术大学**：理科（尤其是物理、量子科学）和前沿科技研究国际知名。  \n",
      "   - **南京大学**：基础学科（天文、地质等）和人文社科传统强校。  \n",
      "\n",
      "**选择建议**：  \n",
      "- 若倾向理工科（如人工智能、航天等），清华或中科大可能是首选。  \n",
      "- 若关注人文社科或医学，北大、复旦更优。  \n",
      "- 地域因素：北京、上海的高校资源更集中，实习机会更多。  \n",
      "\n",
      "**注意**：中国教育部“双一流”建设名单（如清北复交等）可作为参考，但“最好”需结合专业、个人发展目标等综合判断。\n"
     ]
    }
   ],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import MessagesState\n",
    " \n",
    "# llm = ChatOpenAI(\n",
    "#     base_url=\"https://lxxxxx.enovo.com/v1/\", \n",
    "#     api_key=\"sxxxxxxxwW\",\n",
    "#     model_name=\"qwen2.5-instruct\"\n",
    "#     )\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    base_url=\"https://api.deepseek.com\", \n",
    "    api_key=\"sk-3b458ee0624f41e1b8c589e74be23e44\",\n",
    "    model_name=\"deepseek-chat\"\n",
    "    )\n",
    "\n",
    "# 创建节点函数\n",
    "def chatbot(state: MessagesState):\n",
    "    return {\"messages\": [llm.invoke(state[\"messages\"])]}\n",
    "\n",
    "def build_graph():\n",
    "    # 创建graph\n",
    "    graph_builder = StateGraph(MessagesState)\n",
    "    # 添加起始边\n",
    "    graph_builder.add_edge(START, \"chatbot\")\n",
    "    # 添加节点，命名为chatbot\n",
    "    graph_builder.add_node(\"chatbot\", chatbot)\n",
    "    # 添加结束边\n",
    "    graph_builder.add_edge(\"chatbot\", END)\n",
    "    # 代码编译为图\n",
    "    graph = graph_builder.compile()\n",
    "    return graph\n",
    "\n",
    "\n",
    "graph = build_graph()\n",
    "# 生成图的流程图片，写到当前目录下\n",
    "qa_async_png = graph.get_graph().draw_mermaid_png()\n",
    "with open(\"chatbot.png\", \"wb\") as f:\n",
    "    f.write(qa_async_png)\n",
    " \n",
    "# 流式调用大模型\n",
    "for event in graph.stream({\"messages\": [{\"role\": \"user\", \"content\": \"中国最好的大学是哪所？\"}]}):\n",
    "    for value in event.values():\n",
    "        print(\"Assistant:\", value[\"messages\"][-1].content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca5e80c8",
   "metadata": {},
   "source": [
    "我们也可以使用 LangGraph 构建一个 RAG-Agent, RAG(Retrieval-Augmented Generation) 是一种结合了检索和生成两种能力的模型，可以用于处理复杂的问题。它的结构如图\n",
    "\n",
    "<center><img src=\"../assets/langgraph/rag-agent.png\"><RAG-Agent Graph></center>\n",
    "\n",
    "source: https://langgraphcn.org/tutorials/rag/langgraph_agentic_rag/\n",
    "\n",
    "下面仅给出使用 LangGraph 构建RAG-Agent的图流程的代码，此外我们还需要定义 `agent`, `rewrite`, `generate`, `retrieve` 四个节点函数，以及 `RAGState` 状态类，这些内容在LangGraph的官方文档中都有详细的介绍。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "5af6787b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Annotated, Sequence\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langchain_core.messages import BaseMessage\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "\n",
    "class AgentState(TypedDict):\n",
    "    # The add_messages function defines how an update should be processed\n",
    "    # Default is to replace. add_messages says \"append\"\n",
    "    messages: Annotated[Sequence[BaseMessage], add_messages]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a1f9126f",
   "metadata": {},
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'xxx'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mModuleNotFoundError\u001b[39m                       Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlanggraph\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mgraph\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m END, StateGraph, START\n\u001b[32m      2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlanggraph\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mprebuilt\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m ToolNode, tools_condition\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mxxx\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m grade_documents, agent, rewrite, generate, retriever_tool\n\u001b[32m      5\u001b[39m \u001b[38;5;66;03m# Define a new graph\u001b[39;00m\n\u001b[32m      6\u001b[39m workflow = StateGraph(AgentState)\n",
      "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'xxx'"
     ]
    }
   ],
   "source": [
    "from langgraph.graph import END, StateGraph, START\n",
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "from xxx import grade_documents, agent, rewrite, generate, retriever_tool\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(AgentState)\n",
    "\n",
    "# Define the nodes we will cycle between\n",
    "workflow.add_node(\"agent\", agent)  # agent\n",
    "retrieve = ToolNode([retriever_tool])\n",
    "workflow.add_node(\"retrieve\", retrieve)  # retrieval\n",
    "workflow.add_node(\"rewrite\", rewrite)  # Re-writing the question\n",
    "workflow.add_node(\n",
    "    \"generate\", generate\n",
    ")  # Generating a response after we know the documents are relevant\n",
    "# Call agent node to decide to retrieve or not\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# Decide whether to retrieve\n",
    "workflow.add_conditional_edges(\n",
    "    \"agent\",\n",
    "    # Assess agent decision\n",
    "    tools_condition,\n",
    "    {\n",
    "        # Translate the condition outputs to nodes in our graph\n",
    "        \"tools\": \"retrieve\",\n",
    "        END: END,\n",
    "    },\n",
    ")\n",
    "\n",
    "# Edges taken after the `action` node is called.\n",
    "workflow.add_conditional_edges(\n",
    "    \"retrieve\",\n",
    "    # Assess agent decision\n",
    "    grade_documents,\n",
    ")\n",
    "workflow.add_edge(\"generate\", END)\n",
    "workflow.add_edge(\"rewrite\", \"agent\")\n",
    "\n",
    "# Compile\n",
    "graph = workflow.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "7c9c809d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import BaseMessage, AIMessage\n",
    "\n",
    "last_output = { 'agent': { 'messages': [ AIMessage(content='根据搜索结果，我将撰写一份高质量的推文，介绍国内外大厂在Agent领域的最新研究成果。以下是推文内容：\\n\\n---\\n\\n🌟 **国内外大厂Agent研究成果概览** 🌟\\n\\n在智能代理（Agent）领域，国内外科技巨头正不断推动技术的边界，以下是近期的一些重要成果：\\n\\n1. **百度** - 百度在Agent技术方面取得了显著进展，特别是在自然语言处理和多模态交互方面。其最新发布的Agent模型在多个基准测试中表现出色，能够更自然地与用户进行对话，并提供更精准的服务。[了解更多](https://blog.csdn.net/RPAdaren/article/details/145156296)\\n\\n2. **智谱AI** - 智谱AI在Agent的构建和应用方面进行了深入研究，特别是在医疗和教育领域的应用。其最新研究成果显示，Agent能够显著提高医疗诊断的准确性和教育的个性化水平。[了解更多](https://aibook.ren/archives/news-2025-agent-rearch-report)\\n\\n3. **阿里巴巴** - 阿里巴巴在Agent技术的应用方面走在前列，特别是在电商和物流领域。其最新发布的Agent模型能够更高效地处理复杂的订单管理和物流调度，大大提升了用户体验。[了解更多](https://zhuanlan.zhihu.com/p/661790669)\\n\\n4. **谷歌** - 谷歌在Agent领域的研究一直走在世界前列，其最新发布的Agent模型在多任务处理和环境适应性方面取得了突破。这些模型能够在多种场景下自主学习和优化，为用户提供更加智能的服务。[了解更多](https://www.huxiu.com/article/3884949.html)\\n\\n5. **微软** - 微软在Agent技术的评估和优化方面进行了大量研究，其最新发布的评估工具能够更准确地衡量Agent的性能和用户体验。这些工具为开发者提供了宝贵的反馈，帮助他们不断改进Agent模型。[了解更多](https://www.infoq.cn/minibook/bTgj82D3gFJK9ZLRM5Ci)\\n\\n这些研究成果不仅展示了Agent技术的最新进展，也为未来的发展指明了方向。随着技术的不断成熟，Agent将在更多领域发挥重要作用，为用户带来更加智能和便捷的体验。\\n\\n#AI #Agent #科技创新 #智能代理\\n\\n---\\n\\n希望这份推文能够帮助你更好地了解国内外大厂在Agent领域的最新研究成果。如果有任何进一步的问题或需要更多详细信息，请随时告诉我！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 531, 'prompt_tokens': 618, 'total_tokens': 1149, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen2.5-72B-Instruct', 'system_fingerprint': '', 'id': '0196f6db602e49b57593689e2c1d64cb', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--7c6cc76a-482f-467f-ab5d-9f54079bbd56-0', usage_metadata={'input_tokens': 618, 'output_tokens': 531, 'total_tokens': 1149, 'input_token_details': {}, 'output_token_details': {}})]}}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "0ea31c3a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "===== 总输出内容（最后一次迭代的 output） =====\n",
      "\n",
      "('根据搜索结果，我将撰写一份高质量的推文，介绍国内外大厂在Agent领域的最新研究成果。以下是推文内容：\\n'\n",
      " '\\n'\n",
      " '---\\n'\n",
      " '\\n'\n",
      " '🌟 **国内外大厂Agent研究成果概览** 🌟\\n'\n",
      " '\\n'\n",
      " '在智能代理（Agent）领域，国内外科技巨头正不断推动技术的边界，以下是近期的一些重要成果：\\n'\n",
      " '\\n'\n",
      " '1. **百度** - '\n",
      " '百度在Agent技术方面取得了显著进展，特别是在自然语言处理和多模态交互方面。其最新发布的Agent模型在多个基准测试中表现出色，能够更自然地与用户进行对话，并提供更精准的服务。[了解更多](https://blog.csdn.net/RPAdaren/article/details/145156296)\\n'\n",
      " '\\n'\n",
      " '2. **智谱AI** - '\n",
      " '智谱AI在Agent的构建和应用方面进行了深入研究，特别是在医疗和教育领域的应用。其最新研究成果显示，Agent能够显著提高医疗诊断的准确性和教育的个性化水平。[了解更多](https://aibook.ren/archives/news-2025-agent-rearch-report)\\n'\n",
      " '\\n'\n",
      " '3. **阿里巴巴** - '\n",
      " '阿里巴巴在Agent技术的应用方面走在前列，特别是在电商和物流领域。其最新发布的Agent模型能够更高效地处理复杂的订单管理和物流调度，大大提升了用户体验。[了解更多](https://zhuanlan.zhihu.com/p/661790669)\\n'\n",
      " '\\n'\n",
      " '4. **谷歌** - '\n",
      " '谷歌在Agent领域的研究一直走在世界前列，其最新发布的Agent模型在多任务处理和环境适应性方面取得了突破。这些模型能够在多种场景下自主学习和优化，为用户提供更加智能的服务。[了解更多](https://www.huxiu.com/article/3884949.html)\\n'\n",
      " '\\n'\n",
      " '5. **微软** - '\n",
      " '微软在Agent技术的评估和优化方面进行了大量研究，其最新发布的评估工具能够更准确地衡量Agent的性能和用户体验。这些工具为开发者提供了宝贵的反馈，帮助他们不断改进Agent模型。[了解更多](https://www.infoq.cn/minibook/bTgj82D3gFJK9ZLRM5Ci)\\n'\n",
      " '\\n'\n",
      " '这些研究成果不仅展示了Agent技术的最新进展，也为未来的发展指明了方向。随着技术的不断成熟，Agent将在更多领域发挥重要作用，为用户带来更加智能和便捷的体验。\\n'\n",
      " '\\n'\n",
      " '#AI #Agent #科技创新 #智能代理\\n'\n",
      " '\\n'\n",
      " '---\\n'\n",
      " '\\n'\n",
      " '希望这份推文能够帮助你更好地了解国内外大厂在Agent领域的最新研究成果。如果有任何进一步的问题或需要更多详细信息，请随时告诉我！')\n"
     ]
    }
   ],
   "source": [
    "import pprint\n",
    "\n",
    "# 打印最终所有输出内容\n",
    "if last_output:\n",
    "    last_content = last_output['agent'][\"messages\"][0].content\n",
    "    print(\"\\n===== 总输出内容（最后一次迭代的 output） =====\\n\")\n",
    "    pprint.pprint(last_content, indent=2, width=80, depth=None, compact=False)\n",
    "else:\n",
    "    print(\"\\n===== 没有输出 =====\\n\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "lang",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
